#include "programme.h"

programme::programme()
{

}


programme::programme(string nom, bool slt):nom_fic(nom),silencieux(slt)
{

}

bool isID(const string & id)
{
	
	regex_t preg;
	
	const char *noSpecial = "[^_a-zA-Z0-9]";
	const char *alphaBegin ="^[a-zA-Z][a-zA-Z0-9]*";
	regcomp (&preg, noSpecial, REG_NOSUB | REG_EXTENDED);
	int match;
	match=regexec (&preg, id.c_str(), 0, NULL, 0);
	regfree(&preg);
	switch (match)
	{
		case 0: {/*Match: Il y a des caractère spéciaux dans la chaine*/
			return 0;
			break;
		}
		case REG_NOMATCH:{/*NotMatch : Pas de caractères spéciaux*/
			
			//On teste si la chaine commence par un caractère alpha 
			
			regcomp (&preg, alphaBegin, REG_NOSUB | REG_EXTENDED);
			if (regexec (&preg, id.c_str(), 0, NULL, 0) == 0){ regfree(&preg); return 1;}
			else return 0;
			break;
		}
		default : return 0;
	}
}

void Tokenize(const string& stri, vector< string >& tokens, const string& delimiters)
{
	string str=stri;
	 str.erase(std::remove( str.begin(), str.end(), ';' ),str.end() ); 
  // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);
    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

void programme::parser()
{

   ifstream fichier(nom_fic.c_str(), ios::in);  // on ouvre en lecture
   
   int isExec = access(nom_fic.c_str(), X_OK);
   
   stack<int> boucle;
   
   if(fichier && isExec !=0) 
        {
	  
        string ligne;
		int num_ligne=1;
		
		//Creer la fonction Main qui devient la fonction courrante.
		
		fonction main(0,"main", flux_erreur);
        while(getline(fichier,ligne))
		{

			//-!!!!!!!!!!!!!!!!!!!ATTENTION, une instance de fonction doit contenir une référence sur le flux d'erreur

			// Si mot-clef FONCTION -> On Cré une nouvelle fonction qui devient la fonction courrante.
			// Tant qu'on a pas FIN FONCTION ou EOF(si Fonction courrante = Main), on ajout les Instructions à la FONCTION courrante

			//On supprime les espaces avant et après la chaine
			ligne= trim (ligne);
			if (ligne!="")
			{
				int space=ligne.find_first_of(' ');
				string typeInst=ligne.substr(0,space);
				string instr="";
				if(typeInst==ligne) instr="";
				else instr=ligne.substr(space+1);
				
				
				if (typeInst== "FONCTION") 
				{
					stack<int> boucleF;
					num_ligne++;
					if (isID(trim(instr)))
					{
						string nameOfTheFonction = trim(instr);
						/*
							* 
							* 	Actions qu'on effectue avant de parser le contenu de la fonction
							* 
							*/
						fonction F(num_ligne,instr,flux_erreur);
						
						if (!silencieux) cout<<"Analyse fonction \""<<instr<<"\"";
						//On vérifie que les lignes qui suivent sont correctes
						while (getline(fichier,ligne) && trim(ligne)=="") num_ligne++;
						num_ligne++;
						space=ligne.find_first_of(' ');
						typeInst=ligne.substr(0,space);
						string instr;
						if(typeInst==ligne) instr="";
						else instr=ligne.substr(space+1);
						//----!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
						

						if (typeInst=="PARAMETRES") //Si il y a bien la déclaration de paramètres
						{
							vector<string> elements;
							Tokenize(instr,elements," ");
							
							main.addFonction(nameOfTheFonction,elements.size()); //On ajoute la présence de cette fonction au main
							//On vérifie que tout les paramètres sont des ID et on les ajoutent aux paramètres de la fonction
							int i;
							bool well=true;
							for(i=0;i<elements.size();i++)
							{
								
								if(isID(elements[i])) F.addParameter(elements[i]);
								else 
								{
									flux_erreur<<"error :"<<elements[i]<<" n'est pas un nom de variable valide"<<endl;
									well=false;
									break;
								}
							}
							if (!well) //Si une erreur a été rencontrée lors de l'ajout des paramètres
							{
								break;
							}
							
							//Si on trouve le mot clé Début
							while (getline(fichier,ligne) && trim(ligne)=="")  num_ligne++;
							num_ligne++;
							ligne=trim(ligne);
							
							if (ligne=="DEBUT")
							{
								bool returnStatement=false;
								
								
								//On ajoute les noms de fonctions que les instuctions de F pourront appeller
								for(vector<fonction>::iterator i=vect_func.begin();i!=vect_func.end();i++)
								{
								
									F.addFonction((*i).getName(),elements.size()-1);
								}
								
								while (getline(fichier,ligne) && trim(ligne)!="FIN_FONCTION" )
								{
									//On ajoute les instructions a la fonction
									ligne=trim(ligne);
									if(ligne!="") //Si on a une ligne vide, ce n'est pas une instruction
									{
										if(returnStatement)
										{
											flux_erreur <<"ligne "<<num_ligne<<" >error : Code inatteignable :"<<ligne<<endl;
											break;
										}
										space=ligne.find_first_of(' ');
										typeInst=ligne.substr(0,space);
										instr=ligne.substr(space+1);
										
										/*
										* 
										* 
										* Attention Prototype Sans Gestion des Boucles
										* 
										* 
										* 
										*/
										if (!silencieux) cout<<".";
										{
											if (typeInst=="DECLARE" || typeInst=="AFFECTE" || typeInst=="AFFICHE" || typeInst=="APPEL" || typeInst=="REPETE" || typeInst=="FIN_REPETE")
											{
												
												if(typeInst=="REPETE")
												{
													while (getline(fichier,ligne) && trim(ligne)=="")  num_ligne++;
													num_ligne++;
													ligne=trim(ligne);
													if(ligne=="DEBUT") boucleF.push(num_ligne);
													else
													{
														flux_erreur <<" >error : DEBUT de boucle (ligne "<<num_ligne-1<<") manquant"<<endl;
														break;
													}
												}
												if(typeInst=="FIN_REPETE")
												{
													if (!boucleF.empty()) boucleF.pop();
													else 
													{
														flux_erreur <<" >error : FIN_REPETE inattendu (ligne "<<num_ligne<<")"<<endl;
														break;
													}
													
												}
												F.addInstruction(num_ligne,typeInst,instr);
											}
											else
											{
												if (typeInst=="RETOUR")
												{
													
													F.addInstruction(num_ligne,typeInst,instr);
													returnStatement=true;
												}
												else
												{
													stringstream comment;
													comment<<typeInst<<" "<<instr;
													F.addInstruction(num_ligne,"#",comment.str());
												}
											}
										}
									}
									num_ligne++;
								}
								cout<<endl;
								if (trim(ligne)=="FIN_FONCTION" && returnStatement) 
								{
									if(!boucleF.empty()) 
									{
										flux_erreur <<" >error : FIN_REPETE manquant pour la boucle démarrant ligne "<<boucleF.top()<<endl;
									}
									if (!silencieux) cout<<" >Terminée."<<endl;
									//F.listeInstructions();
									addFonction(F);
								}
								else 
								{
									if (!returnStatement)
									{
										flux_erreur <<endl<<" >error : RETOUR manquant"<<endl;
										break;
									}
									else
									{
										flux_erreur <<" >error : FIN_FONCTION manquant"<<endl;
										break;
									}
								}
								
							}
							else
							{
								flux_erreur <<endl<<" >error : DEBUT manquant"<<endl;
								break;
							}
						}
						else
						{
							flux_erreur <<endl<<" >error : mot clef PARAMETRES manquant"<<endl;
							break;
						}
					}
					else
					{
						flux_erreur <<"Line "<<num_ligne<<": "<<"error :"<<instr<<" n'est pas un nom valide"<<endl;
						break;
					}
				}
				else
				{
					if (typeInst=="DECLARE" || typeInst=="AFFECTE" || typeInst=="AFFICHE" || typeInst=="APPEL"|| typeInst=="REPETE" || typeInst=="FIN_REPETE")
					{
						
						if(typeInst=="REPETE")
						{
							while (getline(fichier,ligne) && trim(ligne)=="")  num_ligne++;
							num_ligne++;
							ligne=trim(ligne);
							if(ligne=="DEBUT") boucle.push(num_ligne);
							else
							{
								flux_erreur <<" >error : DEBUT de boucle (ligne "<<num_ligne-1<<") manquant"<<endl;
								break;
							}
						}
						if(typeInst=="FIN_REPETE")
						{
							if (!boucle.empty()) boucle.pop();
							else 
							{
								flux_erreur <<" >error : FIN_REPETE inattendu (ligne "<<num_ligne<<")"<<endl;
								break;
							}
							
						}
						main.addInstruction(num_ligne,typeInst,instr);
					}
					else
					{
						stringstream comment;
						comment<<typeInst<<" "<<instr;
						main.addInstruction(num_ligne,"#",comment.str());
					}
				}
			}


			num_ligne++;
		}
				if(!boucle.empty()) 
				{
					flux_erreur <<" >error : FIN_REPETE manquant pour la boucle démarrant ligne "<<boucle.top()<<endl;
				}
               //Ajout de la fonction Main au programme;
				if (!silencieux) cout<<"Analyse fonction principale"<<endl;
				addFonction(main);
				if (!silencieux) cout<<" >Terminée"<<endl;
				if (!silencieux) cout<<"Résultat de l'analyse:"<<endl;
				if(flux_erreur.str().length()==0) 
				{
					if (!silencieux) cout<<" Ok"<<endl;
					valide = 1;
				}
				else 
				{
					valide = 0;
				}
				fichier.close();
        }
        else
                cerr << "Impossible d'ouvrir le fichier :"<<nom_fic<< endl;
 
		
  cout<<flux_erreur.str();;
}

programmecpp programme::translate()
{
	programmecpp P(nom_fic.substr(0,nom_fic.length()-4),silencieux);
	if(vect_func.empty()) valide=0;
	else
	{
		for(vector<fonction>::iterator it=vect_func.begin(); it!=vect_func.end(); it++)
		{	
			valide=valide && (*it).isValid();
		}
	}
	if(!valide) cout<<">>>Compilation Failed !!<<<"<<endl;
	else
	{
		if (!silencieux) cout<<"Traduction ";
		vector<fonction>::iterator itSurMain;
		for(vector<fonction>::iterator it=vect_func.begin(); it!=vect_func.end(); it++)
		{
			P.addFonctionCPP((*it).translate());
			if (!silencieux) cout<<".";
		}
		if (!silencieux) cout<<endl<<" >Terminée"<<endl;
		
		P.streamize();
		cout<<">>>Successful Compilation<<<"<<endl;
	}
	
	return P;
	
}

void programme::addFonction(const fonction& F)
{
	fonction G=F;
	vect_func.push_back(F);
}

void programme::afficheFonct()
{
	for(vector<fonction>::iterator it=vect_func.begin(); it!=vect_func.end(); it++)
	{
		(*it).listInstructions();
	}
}


programme::~programme()
{

}

